package com.yubest.fs.service.impl;

import com.yubest.fs.bean.*;
import com.yubest.fs.config.FileStorageProperties;
import com.yubest.fs.consts.Consts;
import com.yubest.fs.exception.StorageException;
import com.yubest.fs.util.FileUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.io.*;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;

/**
 * 本地存储服务
 * @author huangweiyu
 * @version V1.0
 * @date 23:57
 **/
public class LocalStorageImpl extends AbstractStorage {

    private static final Logger log = LoggerFactory.getLogger(LocalStorageImpl.class);

    @Override
    public void prepareClient(String bucket) {
        //校验配置信息是否完整
        if (null != getProps() && null != getProps().getLocal()) {
            FileStorageProperties.LocalConfig config = this.getProps().getLocal();
            if (!StringUtils.isEmpty(config.getDir())) {
                return;
            }
        }
        throw new StorageException("local configuration is missing");
    }

    @Override
    public PutResponse upload(PutFileRequest request) {
        PutResponse uploader = new PutResponse();

        FileStorageProperties.LocalConfig config = this.getProps().getLocal();

        FileOutputStream write = null;
        String path = getPath(config.getPrefix(), request.getFileName());
        //获取本地保存地址全路径
        String fullPath = config.getDir() + "/" + path;
        try {
            //服务端保存的文件对象
            File serverFile = new File(fullPath);
            //检测是否存在目录，没有则创建
            if (!serverFile.getParentFile().exists()) {
                serverFile.getParentFile().mkdirs();
            }
            //将上传的文件写入到服务器端
            write = new FileOutputStream(serverFile);
            write.write(request.getPayload());
            //生成meta文件
            createMetaFile(fullPath + Consts.LOCAL_META_FILE_SUFFIX, request);

            uploader.setPath(path);
            uploader.setUrl(this.getAccessUrl(new GetFileRequest().setPath(path)));
        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                log.error("upload file error, filePath:{}", fullPath, e);
            }
            uploader.setCode(Consts.CODE_ERROR);
            uploader.setError(e);
        } finally {
            FileUtil.close(write);
        }
        return uploader;
    }

    private void createMetaFile(String path, PutFileRequest request) {
        delete(new RemoveFileRequest().setPath(path));

        try(BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(path))) {
            Properties properties = new Properties();
            //设置内置属性
            properties.setProperty(Consts.META_FILE_NAME,
                    !StringUtils.isEmpty(request.getFileName()) ? request.getFileName() : "");
            properties.setProperty(Consts.META_FILE_CONTENT_TYPE,
                    !StringUtils.isEmpty(request.getContentType()) ? request.getContentType() : "");
            properties.setProperty(Consts.META_FILE_SIZE, String.valueOf(request.getPayload().length));
            //设置用户属性
            if (null != request.getMeta()) {
                for (Map.Entry<String, String> entry : request.getMeta().entrySet()) {
                    properties.setProperty(Consts.LOCAL_META_USER_VAR_PREFIX + entry.getKey(), entry.getValue());
                }
            }
            //保存meta文件
            properties.store(os, "store file meta");
        } catch (IOException e) {
            if (log.isErrorEnabled()) {
                log.error("upload file create meta file error, file path:{}", path, e);
            }
        }
    }

    @Override
    public FileStream download(GetFileRequest request) {
        FileStorageProperties.LocalConfig config = this.getProps().getLocal();
        String fullPath = config.getDir() + "/" + request.getPath();
        File file = new File(fullPath);
        if (file.exists()) {
            try {
                FileMeta meta = doGetMeta(request);
                return new FileStream(meta, file);
            } catch (IOException e) {
                if (log.isErrorEnabled()) {
                    log.error("download file error, file path:{}", fullPath, e);
                }
                throw new StorageException("download file error, file path:" + fullPath);
            }
        }
        return FileStream.empty();
    }

    @Override
    public String getAccessUrl(GetFileRequest request) {
        FileStorageProperties.LocalConfig config = this.getProps().getLocal();
        return config.getDomain() + "/" + request.getPath();
    }

    @Override
    public FileMeta doGetMeta(GetFileRequest request) {
        FileStorageProperties.LocalConfig config = this.getProps().getLocal();
        //本地文件全路径
        String fileFullPath = config.getDir() + "/" + request.getPath();
        //本地文件对应的meta文件全路径
        String metaFullPath = fileFullPath + Consts.LOCAL_META_FILE_SUFFIX;
        File file = new File(metaFullPath);
        FileMeta meta = new FileMeta();
        if (file.exists()) {
            try(BufferedInputStream is = new BufferedInputStream(new FileInputStream(metaFullPath))) {
                Properties properties = new Properties();
                properties.load(is);
                Enumeration<?> names = properties.propertyNames();
                //遍历读取meta文件里的属性
                while (names.hasMoreElements()) {
                    String name = (String) names.nextElement();
                    if (name.startsWith(Consts.LOCAL_META_USER_VAR_PREFIX)) {
                        name = name.substring(2);
                    }
                    meta.add(name, properties.getProperty(name));
                }
            } catch (IOException e) {
                if (log.isErrorEnabled()) {
                    log.error("download file error, file path:{}", metaFullPath, e);
                }
                throw new StorageException("download file error, file path:" + metaFullPath);
            }
        }
        return meta;
    }

    @Override
    public boolean isFileExists(GetFileRequest request) {
        FileStorageProperties.LocalConfig config = this.getProps().getLocal();
        String fullPath = config.getDir() + "/" + request.getPath();
        File file = new File(fullPath);
        return file.exists();
    }

    @Override
    public boolean delete(RemoveFileRequest request) {
        FileStorageProperties.LocalConfig config = this.getProps().getLocal();
        //删除文件
        String fullPath = config.getDir() + "/" + request.getPath();
        File file = new File(fullPath);
        if (file.exists()) {
            file.delete();
        }
        //删除meta文件
        String metaFullPath = fullPath + Consts.LOCAL_META_FILE_SUFFIX;
        File metaFile = new File(metaFullPath);
        if (metaFile.exists()) {
            metaFile.delete();
        }
        return true;
    }

}
